package com.jichangxiu.framework.config;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.MySqlDialect;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.github.yulichang.injector.MPJSqlInjector;
import com.github.yulichang.interceptor.MPJInterceptor;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.context.JcxContext;
import com.jichangxiu.common.enums.SourceType;
import com.jichangxiu.framework.entity.bo.DynamicSource;
import com.jichangxiu.framework.factory.TransactionFactory;
import com.jichangxiu.framework.handler.BaseMetaObjectHandler;
import com.jichangxiu.framework.interceptor.MybatisLogSqlInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.util.List;
import java.util.Objects;

@Slf4j
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@EnableConfigurationProperties({MybatisPlusProperties.class})
public class MybatisPlusConfig {

    private final DynamicSource dynamicSource = SpringUtil.getBean(DynamicSource.class);

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        log.debug("【MybatisPlusConfig】加载【SqlSessionTemplate】配置");
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    @Autowired
    public SqlSessionFactory sqlSessionFactory(MybatisPlusProperties mybatisPlusProperties) throws Exception {
        log.debug("【MybatisPlusConfig】加载【SqlSessionFactory】配置");
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicSource);
        sqlSessionFactoryBean.setTypeAliasesPackage(Constants.JCX_PROPERTIES.getMyBatisPlusProperties().getAliasesPackage());
        sqlSessionFactoryBean.setTypeEnumsPackage(Constants.JCX_PROPERTIES.getMyBatisPlusProperties().getAliasesPackage());
        // 设置 MyBatis Mapper 文件的扫描路径
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources(Constants.JCX_PROPERTIES.getMyBatisPlusProperties().getMapperLocations()));
        // 设置 MyBatis 全局配置
        GlobalConfig globalConfig = mybatisPlusProperties.getGlobalConfig();
        globalConfig.setMetaObjectHandler(new BaseMetaObjectHandler());
        globalConfig.setSqlInjector(new MPJSqlInjector());
        sqlSessionFactoryBean.setGlobalConfig(globalConfig);
        // 设置XML
        MybatisConfiguration configuration = mybatisPlusProperties.getConfiguration();
        configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        // 设置sql日志
        configuration.addInterceptor(mybatisLogSqlInterceptor());
        // 设置枚举处理器
        sqlSessionFactoryBean.setConfiguration(configuration);
        // 配置插件
        sqlSessionFactoryBean.setPlugins(mybatisPlusInterceptor(), new MPJInterceptor());
        // 配置事务管理器
        sqlSessionFactoryBean.setTransactionFactory(new TransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public ISqlInjector sqlInjector() {
        log.debug("【MybatisPlusConfig】加载【ISqlInjector】配置");
        return new MPJSqlInjector() {
            @Override
            public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
                List<AbstractMethod> list = super.getMethodList(mapperClass, tableInfo);
                // 添加你的方法
                list.add(new InsertBatchSomeColumn());
                return list;
            }
        };
    }

    // Mybatis-Plus 插件
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        log.debug("【MybatisPlusConfig】加载【MybatisPlusInterceptor】配置");
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // 乐观锁插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        // 阻断插件
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
        // 多租户插件
        interceptor.addInnerInterceptor(tenantLineInnerInterceptor());
        return interceptor;
    }

    // 分页插件，自动识别数据库类型
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        log.debug("【MybatisPlusConfig】加载【PaginationInnerInterceptor】配置");
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置数据库类型为 mysql
        SourceType sourceType = Constants.JCX_PROPERTIES.getMyBatisPlusProperties().getSourceType();
        if (sourceType == SourceType.MySQL)
            paginationInnerInterceptor.setDbType(DbType.MYSQL);
        else if (sourceType == SourceType.Oracle)
            paginationInnerInterceptor.setDbType(DbType.ORACLE);
        else if (sourceType == SourceType.SQLServer)
            paginationInnerInterceptor.setDbType(DbType.SQL_SERVER);
        else
            paginationInnerInterceptor.setDbType(DbType.MYSQL);
        // 设置最大单页限制数量，默认 500 条，-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        paginationInnerInterceptor.setDialect(new MySqlDialect());
        return paginationInnerInterceptor;
    }

    // 乐观锁插件
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        log.debug("【MybatisPlusConfig】加载【OptimisticLockerInnerInterceptor】配置");
        return new OptimisticLockerInnerInterceptor();
    }

    // 如果是对全表的删除或更新操作，就会终止该操作
    public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
        log.debug("【MybatisPlusConfig】加载【BlockAttackInnerInterceptor】配置");
        return new BlockAttackInnerInterceptor();
    }

    // 多租户插件
    public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
        log.debug("【MybatisPlusConfig】加载【TenantLineInnerInterceptor】配置");
        return new TenantLineInnerInterceptor(new TenantLineHandler() {

            @Override
            public Expression getTenantId() {
                return new StringValue(JcxContext.getTenantId());
            }

            @Override
            public String getTenantIdColumn() {
                return Constants.JCX_PROPERTIES.getMyBatisPlusProperties().getTenantIdColumn();
            }

            @Override
            public boolean ignoreTable(String tableName) {
                // 优先注解
                if (Objects.nonNull(JcxContext.getEnabledTenant())) {
                    // 如果配置了注解
                    return JcxContext.getEnabledTenant();
                } else {
                    // 如果没有配置注解
                    String noTenantTables = Constants.JCX_PROPERTIES.getMyBatisPlusProperties().getNoTenantTables();
                    if (StrUtil.isNotEmpty(noTenantTables))
                        return ListUtil.toList(noTenantTables.split(",")).contains(tableName);
                }
                return false;
            }
        });
    }

    // 日志插件
    public MybatisLogSqlInterceptor mybatisLogSqlInterceptor() {
        return new MybatisLogSqlInterceptor();
    }

}
